package com.ojoin.ojoin.eb_spider.service.impl;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.URL;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.imageio.ImageIO;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.Point;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.remote.HttpCommandExecutor;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.google.common.base.Function;
import com.ojoin.ojoin.eb_spider.service.CookieService;
import com.ojoin.ojoin.eb_spider.service.WebDriverService;
import com.ojoin.ojoin.eb_spider.utils.DMV3;
import com.ojoin.trade.common.exception.BusinessException;
import com.ojoin.trade.common.utils.CommonUtils;
import com.ojoin.trade.common.utils.IpUtils;
import com.ojoin.trade.common.utils.LogUtils;
import com.ojoin.trade.common.utils.VerityCodeUtils;
import com.ojoin.trade.common.utils.json.JSONUtils;
import com.ojoin.trade.supplyloan_core.constdef.IPlatformUrlDef;

public abstract class BasicWebDriverServiceImpl implements WebDriverService,
		Serializable {

	@Autowired
	@Qualifier("cookieServiceDB")
	// cookieServiceFile
	// cookieServiceDict
	private CookieService cookieService;

	public static Logger log = LoggerFactory
			.getLogger(BasicWebDriverServiceImpl.class);

	private static final long serialVersionUID = 1L;

	ThreadLocal<WebDriver> localdriver = new ThreadLocal<WebDriver>();

	@Override
	public WebDriver getDriver() {
		return localdriver.get();
	}

	public void setDriver(WebDriver driver) {
		localdriver.set(driver);
	}

	protected boolean isDev() {
		return CommonUtils.isDev();
	}

	public WebElement findElement(String pathType, String path) {
		return findElement(null, pathType, path);

	}

	public WebElement findElement(WebElement element, String pathType,
			String path) {

		if (CommonUtils.isEmpty(pathType)) {
			throw new BusinessException(
					"pathType can not be null;it can be id or css ...");
		}

		if (CommonUtils.isEmpty(path)) {
			throw new BusinessException("path is null ,can not find Element");
		}

		By by = getBy(pathType, path);

		if (null == element) {
			return getDriver().findElement(by);
		} else {
			return element.findElement(by);
		}
	}

	public By getBy(String pathType, String path) {
		if (pathType.equals("id")) {
			return By.id(path);

		} else if (pathType.equals("css")) {
			return By.cssSelector(path);

		} else if (pathType.equals("xpath")) {
			return By.xpath(path);
		} else if (pathType.equals("className")) {
			return By.className(path);
		} else if (pathType.equals("tagName")) {
			return By.tagName(path);
		} else {
			throw new BusinessException("pathType is not id or css");
		}
	}

	public WebDriverWait getWebDriverWait(int times) {
		return new WebDriverWait(getDriver(), times);
	}

	public List<WebElement> findElements(WebElement element, String pathType,
			String path) {

		if (CommonUtils.isEmpty(pathType)) {
			throw new BusinessException(
					"pathType can not be null;it can be id or css ...");
		}

		if (CommonUtils.isEmpty(path)) {
			throw new BusinessException("path is null ,can not find Element");
		}

		By by = getBy(pathType, path);

		if (null == element) {
			return getDriver().findElements(by);
		} else {
			return element.findElements(by);
		}

	}

	public List<WebElement> findElements(String pathType, String path) {

		return findElements(null, pathType, path);

	}

	public void click(String pathType, String path) {
		WebElement element = findElement(pathType, path);
		Actions actions = new Actions(getDriver());

		actions.moveToElement(element).click().perform();
		waitForPageLoad();
		waitForAjaxOver();
	}
	
	public void click(String pathType, String path,int sencodes) {
		WebElement element = findElement(pathType, path);
		element.click();
		waitForPageLoad(sencodes);
		waitForAjaxOver(sencodes,0);
	}

	

	public void click(WebElement element, String pathType, String path) {
		WebElement ele = findElement(element, pathType, path);
		ele.click();
		waitForPageLoad();
		waitForAjaxOver();
	}

	public void clickWaitForPage(String pathType, String path) {
		WebElement element = findElement(pathType, path);
		element.click();
		waitForPageLoad();
	}

	public void clickByJS(String pathType, String path) {
		WebElement element = findElement(pathType, path);
		this.clickByJS(element);
	}

	public void clickdblByJS(String pathType, String path) {
		WebElement element = findElement(pathType, path);
		 
		Actions action = new Actions(this.getDriver()); 
		 //action.doubleClick();// 鼠标在当前停留的位置做双击操作 
		action.doubleClick(element).perform();// 鼠标双击指定的元素
		
		
	}
	
	
	
	
	public WebElement waitVisibilityOfElementLocated(String pathType,
			String path) {

		return waitVisibilityOfElementLocated(pathType, path, 60);
	}

	public WebElement waitVisibilityOfElementLocated(String pathType,
			String path, int millisecond) {
		WebDriverWait wait = new WebDriverWait(getDriver(), millisecond);

		return wait.until(ExpectedConditions.visibilityOfElementLocated(getBy(
				pathType, path)));
	}

	public Select findSelect(String pathType, String path) {
		Select select = new Select(findElement(pathType, path));
		return select;
	}

	public Select findSelect(WebElement element) {
		Select select = new Select(element);
		return select;
	}

	public void selectByValue(String pathType, String path, String value) {
		WebElement element = findElement(pathType, path);
		selectByValue(element, value);
	}

	public void selectByValue(WebElement element, String value) {

		findSelect(element).selectByValue(value);
	}

	public void sendKeys(String pathType, String path, String value) {
		WebElement element = findElement(pathType, path);
		sendKeys(element, value);
	}

	public void getNeedCheck(String url) {
		getDriver().get(url);
		if (!getDriver().getCurrentUrl().equals(url)) {
			throw new BusinessException(
					"url has changed ,please check or user method get ");
		}
	}

	public void get(String url) {
		getDriver().get(url);
		checkAlert();
		this.waitForPageLoad();
		this.waitForAjaxOver();
	}

	public void getAndPageLoad(String url) {
		getDriver().get(url);
		checkAlert();
		this.waitForPageLoad();
	}

	public void getAndAjaxOver(String url) {
		getDriver().get(url);
		checkAlert();
		this.waitForAjaxOver();
	}

	public void switchToframe(String name) {
		getDriver().switchTo().frame(name);
	}

	public void switchToDefaultContent() {
		getDriver().switchTo().defaultContent();
	}

	public void switchToframe(WebElement frame) {
		getDriver().switchTo().frame(frame);
	}

	public void sendKeys(WebElement element, String value) {
		this.clearByJS(element);
		element.sendKeys(value);
		this.waitForPageLoad();
		this.waitForAjaxOver();
	}

	public boolean IsExist(String pathType, String path) {
		try {
			this.findElement(pathType, path);
			return true;
		} catch (Exception e) {
			return false;
		}

	}

	public boolean IsExist(WebElement element, String pathType, String path) {
		try {
			this.findElement(element, pathType, path);
			return true;
		} catch (Exception e) {
			return false;
		}

	}

	public boolean IsDisplayed(String pathType, String path) {
		try {
			WebElement element = this.findElement(pathType, path);

			return element.isDisplayed();
		} catch (Exception e) {
			return false;
		}

	}

	public String getText(WebElement element, String pathType, String path) {
		try {
			WebElement el = this.findElement(element, pathType, path);

			return this.getTextByJS(el);
		} catch (Exception e) {
			return null;
		}

	}

	public String getText(String pathType, String path) {
		return getText(null, pathType, path);

	}

	public String getTextHasException(WebElement element, String pathType,
			String path) {
		WebElement el = this.findElement(element, pathType, path);

		return this.getTextByJS(el);

	}

	public String getTextHasException(String pathType, String path) {
		return getText(null, pathType, path);

	}

	public boolean isLogin(final String url) {
		/**
		 * to deal 403 Forbidden
		 * 
		 * get two
		 */
		getDriver().get(url);
		getDriver().get(url);
		String u = getDriver().getCurrentUrl();
		return u.equals(url);

	}

	public int getDefaultDriverTimeOut() {
		return 5;
	}

	public String getSaveCookieDirectory() {

		return System.getProperties().getProperty("user.home") + File.separator
				+ "Downloads" + File.separator + "webdriver";
	}

	@Override
	public void saveCookies(String uid) throws IOException {
		Set<Cookie> cookies = getDriver().manage().getCookies();

		cookieService.saveCookies(uid, cookies);
	}

	@Override
	public void close() {
		if (!CommonUtils.isEmpty(getDriver())) {
			getDriver().quit();
			setDriver(null);
		}
	}

	public void waitForPageLoad() {
		waitForPageLoad(30);
	}

	public void waitForPageLoad(int sencodes) {
		WebDriverWait wait = new WebDriverWait(getDriver(), sencodes);
		wait.until(isPageLoaded());
	}
	
	public void checkAlert() {
		try {
			WebDriverWait wait = new WebDriverWait(getDriver(), 2);
			wait.until(ExpectedConditions.alertIsPresent());
			Alert alert = getDriver().switchTo().alert();
			alert.accept();
		} catch (Exception e) {
			// exception handling
		}

	}

	public String getAlertText() {
		try {
			WebDriverWait wait = new WebDriverWait(getDriver(), 2);
			wait.until(ExpectedConditions.alertIsPresent());
			Alert alert = getDriver().switchTo().alert();

			return alert.getText();
		} catch (Exception e) {
			return "";
		}

	}

	private Function<WebDriver, Boolean> isPageLoaded() {
		return new Function<WebDriver, Boolean>() {
			public Boolean apply(WebDriver driver) {
				return ((JavascriptExecutor) driver).executeScript(
						"return document.readyState").equals("complete");
			}
		};
	}

	public void waitForAjaxOver(int senconds,int statues) {
		WebDriverWait wait = new WebDriverWait(getDriver(), senconds);
		wait.until(isAjaxActive(statues));
	}

	public void waitForAjaxOver() {
		waitForAjaxOver(30,0);
	}

	
	public void executor(String script) {
		JavascriptExecutor executor = (JavascriptExecutor) getDriver();
		executor.executeScript(script);
	}

	public Function<WebDriver, Boolean> isAjaxActive(int statues) {
		final int sta = statues;
		return new Function<WebDriver, Boolean>() {
			public Boolean apply(WebDriver driver) {
				JavascriptExecutor executor = (JavascriptExecutor) driver;
				boolean isdefined = (Boolean) executor
						.executeScript("return window.jQuery != undefined");
				if (isdefined) {
					return (Boolean) executor
							.executeScript("return jQuery.active ==  "+sta);
				}
				return true;
			}
		};
	}

	private Function<WebDriver, Boolean> isAjaxActive() {
		return new Function<WebDriver, Boolean>() {
			public Boolean apply(WebDriver driver) {
				JavascriptExecutor executor = (JavascriptExecutor) driver;
				boolean isdefined = (Boolean) executor
						.executeScript("return window.jQuery != undefined");
				if (isdefined) {
					return ajaxActive(executor);
				}
				return true;
			}
		};
	}

	private boolean ajaxActive(JavascriptExecutor executor) {

		return (Boolean) executor
				.executeScript("return window.jQuery != undefined && jQuery.active == 0");
	}

	@Override
	public byte[] getIMGByte(Object o) {
		WebElement web = (WebElement) o;
		byte[] bytes = null;
		Point location = web.getLocation();
		Dimension size = web.getSize();
		// 创建全屏截图。
		BufferedImage originalImage;
		BufferedImage buff = null;
		ByteArrayOutputStream bout = new ByteArrayOutputStream();

		try {
			originalImage = ImageIO.read(new ByteArrayInputStream(
					takeScreenshot()));
			buff = originalImage.getSubimage(location.getX(), location.getY(),
					size.getWidth(), size.getHeight());

			ImageIO.write(buff, "jpg", bout);

			bytes = bout.toByteArray();

			/*
			 * bytes = new byte[length];
			 * 
			 * bout.write(bytes);
			 */

		} catch (IOException e) {

			e.printStackTrace();
		}

		return bytes;
	}

	public void saveIMG(WebElement web, String filename) {
		Point location = web.getLocation();
		Dimension size = web.getSize();
		// 创建全屏截图。
		BufferedImage originalImage;
		BufferedImage buff = null;

		try {
			originalImage = ImageIO.read(new ByteArrayInputStream(
					takeScreenshot()));
			buff = originalImage.getSubimage(location.getX(), location.getY(),
					size.getWidth(), size.getHeight()

			);

			ImageIO.write(buff, "jpg", new File(filename));
		} catch (IOException e) {

			e.printStackTrace();
		}

	}

	public byte[] takeScreenshot() throws IOException {
		TakesScreenshot takesScreenshot = (TakesScreenshot) getDriver();
		return takesScreenshot.getScreenshotAs(OutputType.BYTES);
	}

	public <T> T execJavaScript(String paramString) {
		JavascriptExecutor executor = (JavascriptExecutor) getDriver();
		return (T) executor.executeScript(paramString);
	}

	public <T> T execJavaScript(String paramString, Object[] paramArrayOfObject) {
		JavascriptExecutor executor = (JavascriptExecutor) getDriver();
		return (T) executor.executeScript(paramString, paramArrayOfObject);
	}

	public String clickByJS(WebElement store_date) {
		String script = "arguments[0].click();";

		String outcome = (String) ((JavascriptExecutor) getDriver())
				.executeScript(script, store_date);

		this.waitForAjaxOver();
		this.waitForPageLoad();

		return outcome;
	}

	
	public String clickdblByJS(WebElement store_date) {
		String script = "arguments[0].dblclick();";

		String outcome = (String) ((JavascriptExecutor) getDriver())
				.executeScript(script, store_date);

		this.waitForAjaxOver();
		this.waitForPageLoad();

		return outcome;
	}

	
	
	public String clearByJS(WebElement store_date) {
		String script = "arguments[0].value = '';";

		String outcome = (String) ((JavascriptExecutor) getDriver())
				.executeScript(script, store_date);

		return outcome;
	}

	public Set<Cookie> getCookiesByJS(WebElement document) {
		String script = "document.cookie;";

		Object outcome = ((JavascriptExecutor) getDriver()).executeScript(
				script, document);

		Set<Cookie> set = new HashSet<Cookie>();

		if (!CommonUtils.isEmpty((String) outcome)) {
			String[] strs = ((String) outcome).split(";");
			for (int i = 0; i < strs.length; i++) {
				String str = strs[i];
				String[] s = str.split("=");

				Cookie cookie = new Cookie(s[0], s[1]);

				set.add(cookie);
			}
		}

		return set;
	}

	// JQuery的动画效果
	public void offAnimation() {
		((JavascriptExecutor) getDriver()).executeScript("jQuery.fx.off=true");
	}

	public void getScreenShot() {
		if (getDriver() instanceof TakesScreenshot) {
			TakesScreenshot screenshotTaker = (TakesScreenshot) getDriver();
			// File file = screenshotTaker.getScreenshotAs();
		}
	}

	private Function<WebDriver, Boolean> haveMoreThanOneOption(final By element) {
		return new Function<WebDriver, Boolean>() {
			public Boolean apply(WebDriver driver) {
				WebElement webElement = driver.findElement(element);
				if (webElement == null) {
					return false;
				} else {
					int size = webElement.findElements(By.tagName("option"))
							.size();
					return size >= 1;
				}
			}
		};
	}

	public String getTextByJS(WebElement element, String pathType, String path) {

		WebElement ele = this.findElement(element, pathType, path);

		return getTextByJS(ele);
	}

	public String getTextByJS(String pathType, String path) {

		return getTextByJS(null, pathType, path);
	}

	public String getTextByJS(WebElement store_date) {
		String script = "arguments[0].removeAttribute('disabled');arguments[0].removeAttribute('readonly');return arguments[0].innerHTML";

		String outcome = (String) ((JavascriptExecutor) getDriver())
				.executeScript(script, store_date);

		return CommonUtils.trimHtml(outcome);

	}

	@Override
	public String getVerifyCode(Object o) throws Exception {
		WebElement verifyE = (WebElement) o;

		String code = "";
		if (null != verifyE) {
			byte[] imgbytes = this.getIMGByte(verifyE);

			if (null != imgbytes) {
				code = VerityCodeUtils.getVerifyCode(
						VerityCodeUtils.VERIFYTYPE_BYTE, null, imgbytes);

				if (CommonUtils.isEmpty(code)) {
					throw new BusinessException(
							"get VerifyCode error,VerifyCode is null");
				}
				// LogUtils.info(log,getPlatformUser().getPlatformName()+"verifyCode->"
				// +
				// code);
			}

		}

		return code;
	}

	@Override
	public String getVerifyCodeByDMV3(Object o) throws Exception {
		WebElement verifyE = (WebElement) o;

		String code = "";
		if (null != verifyE) {
			byte[] imgbytes = this.getIMGByte(verifyE);

			if (null != imgbytes) {

				Map map = DMV3.getVerifyCode(imgbytes);
				code = (String) map.get("data");

			}

		}

		return code;
	}

	@Override
	public boolean initCookies(String uid, String platformName)
			throws IOException, Exception {

		Set<Cookie> cookies = cookieService.getCookies(uid);

		if (CommonUtils.isEmpty(cookies)) {
			LogUtils.info(log, "[%s]cookies登陆失败,cookie不存在", uid);
			return false;
		}

		getDriver().manage().deleteAllCookies();

		Iterator<Cookie> itc = cookies.iterator();
		while (itc.hasNext()) {
			Cookie cookie = itc.next();
			try {
				if (!cookie.getName().equals("_cusenoek")) {
					getDriver().manage().addCookie(cookie);
				}

				// }
			} catch (Exception e) {
				LogUtils.info(log, "忽略 cookie  { %s }" + cookie);
			}
		}

		return true;
	}

	@Override
	public boolean createByCookie(String uid, String platformName)
			throws Exception {

		
		List<String> li = IPlatformUrlDef.mapUrl.get(platformName);

		if (CommonUtils.isEmpty(li)) {
			return false;
		}
		int key = (int) (Math.random() * li.size());

		String logedurl = li.get(key);

		boolean flag = this.isLogin(logedurl);
		
        if(!flag){
        	flag = initCookies(uid, platformName);

    		if (!flag) {
    			return false;
    		}
        }else {
        	return true ;
        }
		
		
		/*
		 * Iterator<Cookie> ittt = driver.manage().getCookies().iterator();
		 * 
		 * while (ittt.hasNext()) { Cookie cookie = ittt.next();
		 * LogUtils.info(log, cookie.toString());
		 * 
		 * }
		 */

		if (this.isLogin(logedurl)) {
			LogUtils.info(log, "[%s]cookies登陆成功", uid);

			return true;
		} else {
			LogUtils.info(log, "[%s]cookies登陆失败", uid);
			return false;

		}

	}

	@Override
	public void initRemoteWebDriver() throws Exception {
		initRemoteWebDriver(null);

	}

	public String getproxyId(String sessionId) {
		String ret = null;
		if (!(getDriver() instanceof RemoteWebDriver)) {
			ret = IpUtils.getLocalIp();
		} else {

			RemoteWebDriver rweb = (RemoteWebDriver) getDriver();
			HttpCommandExecutor httpCE = (HttpCommandExecutor) rweb
					.getCommandExecutor();
			String hostName = httpCE.getAddressOfRemoteServer().getHost();
			int port = httpCE.getAddressOfRemoteServer().getPort();

			try {
				if (null == sessionId) {
					sessionId = rweb.getSessionId().toString();
				}
				HttpHost host = new HttpHost(hostName, port);
				HttpClient client = HttpClientBuilder.create().build();
				URL sessionURL = new URL("http://" + hostName + ":" + port
						+ "/grid/api/testsession?session=" + sessionId);
				BasicHttpEntityEnclosingRequest r = new BasicHttpEntityEnclosingRequest(
						"POST", sessionURL.toExternalForm());
				HttpResponse response = client.execute(host, r);

				String jsonStr = IOUtils.toString(response.getEntity()
						.getContent());
				Map<String, String> ojb = JSONUtils.parseObject(jsonStr,
						Map.class);
				String url = ojb.get("proxyId");

				URL myURL = new URL(url);
				if ((myURL.getHost() != null) && (myURL.getPort() != -1)) {
					ret = myURL.getHost();
				}

			} catch (Exception e) {
				LogUtils.info(log, "获取运行节点IP异常");
			}
		}
		return ret;
	}

}
